package io.mqttpush.mqttserver.config;

import io.netty.channel.Channel;
import io.netty.handler.codec.mqtt.MqttConnectMessage;
import io.netty.handler.codec.mqtt.MqttMessage;
import io.netty.handler.codec.mqtt.MqttQoS;
import io.netty.util.AttributeKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 管理设备号
 *
 * @author tianzhenjiu
 * @date 2020/4/28 14:18
 */
public class DeviceManager {

    Logger logger = LoggerFactory.getLogger(getClass());


    /**
     * 我把channelTopics 写成channelId 尽然NoClassDef!!!
     * <p>
     * 以下都是channel attr
     */
    public final AttributeKey<String> channelIdAttr = AttributeKey.newInstance("channelId");

    public final AttributeKey<Map<String, MqttQoS>> channelTopics = AttributeKey.newInstance("channelTopics");

    public final AttributeKey<String> userInfoAttr = AttributeKey.newInstance("userInfo");


    final ConcurrentHashMap<String, Channel> id2Channels = new ConcurrentHashMap<>();


    static DeviceManager deviceManager = new DeviceManager();


    /**
     * 单例模式
     */
    private DeviceManager() {
    }

    public static DeviceManager getDeviceManager() {
        return deviceManager;
    }


    /**
     * 登录
     *
     * @param userInfo
     * @param deviceId
     * @param channel
     */
    public void deviceLogin(String userInfo, String deviceId, Channel channel) {


        if (channel == null) {
            logger.info("登录channel为空");
            return;
        }

        if (!channel.isActive()) {
            logger.info("登录channel不可用");
            return;
        }


        Channel oldChannel = id2Channels.put(deviceId, channel);

        if (oldChannel != null && oldChannel.isActive()) {

            oldChannel.close().addListener((listen) -> {
                logger.info("device 关闭旧的{},{},{},", deviceId, userInfo, channel);
            });
        }

        /**
         *
         * 设置channel属性
         *
         * 设备号
         * 用户信息
         * 订阅的主题
         *
         */
        channel.attr(channelIdAttr).set(deviceId);
        channel.attr(userInfoAttr).set(userInfo);
        channel.attr(channelTopics).set(new HashMap<>(4));

        logger.info("device 登录{},{},{},", deviceId, userInfo, channel);


    }


    /**
     * 获取设备号
     * 从连接报文获取设备号
     * 或者从channel获取设备号
     *
     * @param channel
     * @param mqttConnectMsg
     * @return
     */
    public String deviceId(Channel channel, MqttMessage mqttConnectMsg) {


        String deviceId = null;
        if (mqttConnectMsg instanceof MqttConnectMessage) {
            MqttConnectMessage mqttConnectMessage = (MqttConnectMessage) mqttConnectMsg;
            deviceId = mqttConnectMessage.payload().clientIdentifier();
            if (deviceId == null) {
                logger.info("不合法的报文?devideId缺失");
                throw new IllegalStateException("不合法的报文?devideId缺失");
            }
            return deviceId;
        }


        return deviceId(channel);

    }

    /**
     * 从channel属性获取设备号
     *
     * @param channel
     * @return
     */
    public String deviceId(Channel channel) {

        if (channel == null) {
            return null;
        }

        if (!channel.hasAttr(channelIdAttr)) {
            return channel.id().asShortText();
        }

        return channel.attr(channelIdAttr).get();

    }


    public Boolean isLogin(Channel channel){

        if (channel == null) {
            return false;
        }

        return channel.hasAttr(userInfoAttr);
    }
    /**
     * 获取设备绑定的用户信息
     *
     * @param channel
     * @return
     */
    public String userInfo(Channel channel) {

        if (channel == null) {
            return null;
        }

        if (!isLogin(channel)) {
            return null;
        }

        return channel.attr(userInfoAttr).get();

    }


    /**
     * 获取设备的channel
     * <p>
     * 如果指定的设备号对于的channel不存在了就直接移除
     *
     * @param deviceId
     * @return
     */
    public Channel deviceChannel(String deviceId) {


        if (!id2Channels.containsKey(deviceId)) {
            logger.info("the  device {} is not exists", deviceId);
            return null;
        }

        Channel channel = id2Channels.get(deviceId);
        if (channel == null || !channel.isActive()) {
            logger.info("the  device is not active{}", deviceId);
            id2Channels.remove(deviceId);
            return null;
        }

        return channel;

    }


    public void deviveLoginOut(String deviceId, Channel channel) {


        logger.info("设备退出{}->{}", deviceId, channel);


        /**
         * 取消订阅
         */
        Map<String, MqttQoS> topicQoss = channel.attr(channelTopics).get();
        if (topicQoss != null && !topicQoss.isEmpty()) {

            Map<String, MqttQoS> topicQossCopy = new HashMap<>(topicQoss.size());
            topicQossCopy.putAll(topicQoss);

            TopicManager topicManager = TopicManager.getInstance();

            topicQossCopy.forEach((k, v) -> {
                topicManager.unSubscribe(k, channel);
            });

            topicQossCopy.clear();
            topicQossCopy = null;

        }
        if (channel != null) {

            if (channel.isActive()) {
                Channel newChannel = id2Channels.remove(deviceId);
                if (!newChannel.equals(channel)) {
                    id2Channels.put(deviceId, newChannel);
                }

                channel.attr(channelIdAttr).set(null);
                channel.attr(userInfoAttr).set(null);
                channel.attr(channelTopics).set(null);
            }


        }

    }

}
